package com.easydisk.user.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easydisk.common.redis.RedisConstant;
import com.easydisk.common.redis.RedisKeyPrefix;
import com.easydisk.common.util.JwtUtil;
import com.easydisk.common.util.UUIDUtil;
import com.easydisk.constant.CommonConstant;
import com.easydisk.constant.FileConstant;
import com.easydisk.constant.UserConstant;
import com.easydisk.model.req.AddRootFolderReq;
import com.easydisk.user.UserStatus;
import com.easydisk.user.feignclient.FileFeignClient;
import com.easydisk.user.dao.UserAndRoleMapper;
import com.easydisk.user.dao.UserMapper;
import com.easydisk.user.model.entity.User;
import com.easydisk.user.model.entity.UserAndRole;
import com.easydisk.user.model.req.RegisterReq;
import com.easydisk.user.service.MailService;
import com.easydisk.user.service.UserService;
import com.easydisk.common.util.IdGenerator;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.mail.MessagingException;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserAndRoleMapper userAndRoleMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private FileFeignClient fileFeignClient;

    @Autowired
    private MailService mailService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(RegisterReq registerReq) {
        // 判断用户名是否存在
        QueryWrapper<User> query = new QueryWrapper<>();
        query.eq("username", registerReq.getUsername());
        User exit = userMapper.selectOne(query);
        if (exit != null) {
            throw new RuntimeException("用户名重复,新增用户失败");
        }
        // 判断邮箱是否重复
        long emailCnt = this.count(new QueryWrapper<User>().eq("email", registerReq.getEmail()));
        if (emailCnt != 0) {
            throw new RuntimeException("邮箱重复,新增用户失败");
        }
        if (StringUtils.isNotBlank(registerReq.getMobile())) {
            // 判断手机号是否重复
            long mobileCnt = this.count(new QueryWrapper<User>().eq("mobile", registerReq.getMobile()));
            if (mobileCnt != 0) {
                throw new RuntimeException("手机号重复,新增用户失败");
            }
        }

        // 拼装用户信息
        User user = new User();
        String encode = passwordEncoder.encode(registerReq.getPassword());
        Long userId = IdGenerator.nextId();
        user.setId(userId);
        user.setUsername(registerReq.getUsername());
        user.setMobile(registerReq.getMobile());
        user.setEmail(registerReq.getEmail());
        user.setPassword(encode);
        user.setDiskMaxSize(FileConstant.DEFAULT_MAX_DISK_SIZE);
        user.setStatus(UserConstant.UserStatusEnum.YES.getCode());
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        //插入用户信息
        userMapper.insert(user);

        // 拼装用户角色信息
        UserAndRole userAndRole = new UserAndRole();
        userAndRole.setId(IdGenerator.nextId());
        userAndRole.setUserId(userId);
        //TODO 这里直接写死角色id
        userAndRole.setRoleId(2L);
        // 插入用户角色信息
        userAndRoleMapper.insert(userAndRole);

        AddRootFolderReq addRootFolderReq = new AddRootFolderReq();
        addRootFolderReq.setUserId(userId);
        addRootFolderReq.setFolderName(FileConstant.ALL_FILE_CN_STR);
        addRootFolderReq.setParentId(FileConstant.ROOT_FOLDER_PARENT_ID);
        // feign调用远程接口。创建用户的root文件夹
        fileFeignClient.addRootFolder(addRootFolderReq);

    }

    @Override
    public void sendEmailCode(String email) {
        User byEmail = getByEmail(email);
        if (byEmail == null) {
            throw new RuntimeException("用户邮箱不存在");
        }
        // 发送邮箱验证码
        // 生成6位随机数字验证码
        String code = String.valueOf((int)((Math.random() * 9 + 1) * Math.pow(10,5)));
        String emailTemplate = "<!DOCTYPE html>\n" +
                "<html lang=\"en\">\n" +
                "<head>\n" +
                "    <meta charset=\"UTF-8\">\n" +
                "    <style>\n" +
                "        body {\n" +
                "            text-align: left;\n" +
                "        }\n" +
                "        p{\n" +
                "            font-weight:bold;\n" +
                "            font-size: 26px;\n" +
                "        }\n" +
                "    </style>\n" +
                "    <title></title>\n" +
                "</head>\n" +
                "<body>\n" +
                "<div id=\"welcome\">\n" +
                "    <h3><span>[易云盘]邮箱验证码</h3>\n" +
                "    <div>您的验证码:  <p><span>" + code + "</span>\n" +
                "    </p>您正在进行身份验证,验证码有效期5分钟。</div>\n" +
                "    <div>请勿向他人泄漏验证码,如非本人操作,请忽略。</div>\n" +
                "</div>\n" +
                "</body>\n" +
                "</html>";
        try {
            mailService.sendHtmlMail(email, "[易云盘]邮箱验证码", emailTemplate);
        } catch (MessagingException e) {
            throw new RuntimeException(e.getMessage());
        }
        String key = RedisKeyPrefix.EM_CODE.getKey() + email;
        // 验证码保存在redis，设置过期时间
        redisTemplate.opsForValue().set(key, code,
                RedisConstant.DEFAULT_EXPIRE_TIMES, TimeUnit.SECONDS);
    }

    /**
     * 校验邮箱验证码是否正确
     * @param email
     * @param code
     * @return token,发放token，允许用户重置密码
     */
    @Override
    public String  checkEmailCode(String email, String code) {
        String key = RedisKeyPrefix.EM_CODE.getKey() + email;
        // 判断redis验证码是否过期
        long expire = redisTemplate.opsForValue().getOperations().getExpire(key);
        if (expire == -2) {
            throw  new RuntimeException("验证码已过期，请重新获取");
        }
        String redisCode = (String)redisTemplate.opsForValue().get(key);
        // 判断用户输入的验证码和redis的验证码是否正确
        if (StringUtils.isBlank(redisCode)) {
            throw  new RuntimeException("验证码错误,请重新填写");
        }
        if (!StringUtils.equals(code, redisCode)) {
            throw new RuntimeException("验证码错误");
        }
        // 发放token
        String token = UUIDUtil.getUUID().toUpperCase();
        String emailKey = RedisKeyPrefix.EMAIL_FORGET.getKey() + email;
        // 保存 token到redis,设置过期时间
        redisTemplate.opsForValue().set(emailKey, token, RedisConstant.DEFAULT_EXPIRE_TIMES, TimeUnit.SECONDS);
        return token;
    }

    /**
     * 根据邮箱和token,重置密码
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resetPasswordByEmail(String email, String token, String newPassword) {
        String key = RedisKeyPrefix.EMAIL_FORGET.getKey() + email;
        String redisToken = (String)redisTemplate.opsForValue().get(key);
        // 判断redis是否存在该key
        if (StringUtils.isBlank(redisToken)) {
            throw new RuntimeException("重置密码失败");
        }
        // 判断redis的token和用户传的token是否一致
        if (!StringUtils.equals(redisToken, token)) {
            throw new RuntimeException("重置密码失败");
        }
        // 执行更新密码操作
        String encode = passwordEncoder.encode(newPassword);
        User byEmail = getByEmail(email);
        if (byEmail == null) {
            throw new RuntimeException("用户邮箱不存在,重置密码错误");
        }
        byEmail.setPassword(encode);
        userMapper.updateById(byEmail);
        // 删除 redis的token
        redisTemplate.delete(key);
        // 删除 redis的邮箱验证码code
        redisTemplate.delete(RedisKeyPrefix.EM_CODE.getKey() + email);
    }

    /**
     * 根据用户邮箱查找用户，不存在返回null
     * @param email
     * @return
     */
    @Override
    public User getByEmail(String email) {
        // 查询数据库是否存在该email
        List<User> userList = this.list(new QueryWrapper<User>().eq("email", email));
        if (CollectionUtils.isEmpty(userList)) {
            return null;
        }
        if (userList.size() >= 2) {
            throw new RuntimeException("用户邮箱重复,请联系管理员");
        }
        return userList.get(0);
    }

    @Override
    public String login(String username, String password) {
        List<User> userList = this.list(new QueryWrapper<User>()
                .eq("username", username));
        if (CollectionUtils.isEmpty(userList)) {
            throw  new RuntimeException("账号或者密码错误");
        }
        User user = userList.get(0);
        //验证管理员密码
        if (!passwordEncoder.matches(password,user.getPassword())) {
            throw  new RuntimeException("账号或者密码错误");
        }
        if (user.getStatus().equals(UserStatus.DEACTIVATION.getCode())) {
            throw new RuntimeException("账号已被封禁,请申请找回账号");
        }
        //验证通过，获取用户
        //生成token
        return JwtUtil.generateToken(user.getUsername(), CommonConstant.LOGIN_USER_ID,
                user.getId(),CommonConstant.ONE_DAY_LONG);
    }
}